import os
import tkinter as tk
from tkinter import ttk, filedialog, messagebox, font
import logging
import sys

sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

from duplicate_detector.duplicate_detector import DuplicateDetector
from config_manager.config_manager import ConfigManager
from scanner.scanner import Scanner
from hash_calculator.hash_calculator import HashCalculator

# 设置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)

# 中英文映射
DEPTH_MAPPING = {
    'recursive': '递归',
    'shallow': '浅层',
    '0': '0层',
    '1': '1层',
    '2': '2层',
    '3': '3层',
    '4': '4层',
    '5': '5层'
}

ALGORITHM_MAPPING = {
    'md5': 'MD5',
    'sha1': 'SHA1'
}

SORT_MAPPING = {
    'modified_time': '修改时间',
    'created_time': '创建时间',
    'accessed_time': '访问时间',
    'size': '文件大小',
    'name': '文件名称'
}

ORDER_MAPPING = {
    'ascending': '升序',
    'descending': '降序'
}

# 反向映射
REVERSE_DEPTH_MAPPING = {v: k for k, v in DEPTH_MAPPING.items()}
REVERSE_ALGORITHM_MAPPING = {v: k for k, v in ALGORITHM_MAPPING.items()}
REVERSE_SORT_MAPPING = {v: k for k, v in SORT_MAPPING.items()}
REVERSE_ORDER_MAPPING = {v: k for k, v in ORDER_MAPPING.items()}

class DuplicateFileFinderUI:
    def __init__(self, root):
        self.root = root
        self.root.title('重复文件查找工具')
        self.root.resizable(True, True)

        # 创建配置管理器
        self.config_manager = ConfigManager('config.json')
        self.config_manager.load_config()
        self.config = self.config_manager.get_config()

        # 加载窗口大小和位置配置
        self._load_window_config()

        # 绑定窗口关闭事件
        self.root.protocol("WM_DELETE_WINDOW", self.on_closing)

        # 绑定窗口大小变化事件
        self.root.bind("<Configure>", self.on_configure)
        
        # 配置样式
        style = ttk.Style()
        # 优化删除按钮样式，提高文字可读性
        style.configure('Danger.TButton', 
                       foreground='#721c24',  # 深红色文字
                       background='#f8d7da',  # 浅红色背景
                       font=('Arial', 9, 'bold'),  # 粗体字体
                       borderwidth=1,
                       relief='solid')
        # 设置按钮悬停状态
        style.map('Danger.TButton',
                  foreground=[('active', '#491217'),   # 悬停时更深的红色
                             ('pressed', '#2d0a0f')],  # 按下时最深的红色
                  background=[('active', '#f1aeb5'),   # 悬停时的背景色
                             ('pressed', '#e9909a')])  # 按下时的背景色

        # 初始化变量
        self.directory_list = self.config.get('directories', [os.getcwd()])
        self.scan_depth_var = tk.StringVar(value=self.config['scan_depth'])
        self.hash_algorithm_var = tk.StringVar(value=self.config['hash_algorithm'])
        self.sort_by_var = tk.StringVar(value=self.config['sort_by'])
        self.sort_order_var = tk.StringVar(value=self.config['sort_order'])
        self.show_empty_var = tk.BooleanVar(value=self.config['show_empty_files'])
        self.delete_confirm_var = tk.BooleanVar(value=self.config['delete_confirmation'])
        self.stop_scan_flag = False  # 停止扫描标志
        
        # 扫描模式变量（新增）
        scan_mode = self.config.get('scan_mode', 'file')
        # 向后兼容：如果配置中有scan_directory_mode，优先使用
        if self.config.get('scan_directory_mode', False):
            scan_mode = 'directory'
        self.scan_mode_var = tk.StringVar(value=scan_mode)
        
        # 保持向后兼容的变量
        self.scan_only_dirs_var = tk.BooleanVar(value=self.config.get('scan_only_directories', False))
        self.scan_directory_mode_var = tk.BooleanVar(value=self.config.get('scan_directory_mode', False))
        self.compare_filename_var = tk.BooleanVar(value=self.config.get('compare_filename', False))
        self.compare_size_var = tk.BooleanVar(value=self.config.get('compare_size', True))
        
        # 目录相似度比对变量（新增）
        self.use_similarity_var = tk.BooleanVar(value=self.config.get('use_similarity_comparison', False))
        self.similarity_threshold_var = tk.StringVar(value=self.config.get('similarity_threshold', '95%'))

        self.duplicate_groups = []
        self.checked_items = {}  # item_id -> True
        self.group_tags = []  # 存储每组的所有item_id
        self.group_headers = {}  # group_index -> header_item_id
        self.item_to_group = {}  # item_id -> group_index
        self.group_checkboxes = {}  # header_item_id -> checkbox_state

        # 创建UI组件
        self.create_widgets()

    def create_widgets(self):
        # 创建主框架
        main_frame = ttk.Frame(self.root, padding='10')
        main_frame.pack(fill=tk.BOTH, expand=True)

        # 配置区域
        config_frame = ttk.LabelFrame(main_frame, text='配置', padding='10')
        config_frame.pack(fill=tk.X, pady=5)

        # 目录选择
        ttk.Label(config_frame, text='扫描目录:').grid(row=0, column=0, sticky=tk.W, pady=2)

        # 目录列表框
        dir_frame = ttk.Frame(config_frame)
        dir_frame.grid(row=0, column=1, sticky=tk.EW, pady=2)
        self.dir_listbox = tk.Listbox(dir_frame, width=50, height=4)
        self.dir_listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        scrollbar = ttk.Scrollbar(dir_frame, orient=tk.VERTICAL, command=self.dir_listbox.yview)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        self.dir_listbox.config(yscrollcommand=scrollbar.set)

        # 目录按钮
        dir_btn_frame = ttk.Frame(config_frame)
        dir_btn_frame.grid(row=0, column=2, padx=5, pady=2)
        ttk.Button(dir_btn_frame, text='添加目录', command=self.add_directory).pack(fill=tk.X, pady=2)
        ttk.Button(dir_btn_frame, text='删除目录', command=self.remove_directory).pack(fill=tk.X, pady=2)

        # 扫描深度
        ttk.Label(config_frame, text='扫描深度:').grid(row=1, column=0, sticky=tk.W, pady=2)
        depth_values = list(DEPTH_MAPPING.values())
        self.depth_combobox = ttk.Combobox(config_frame, values=depth_values, state='readonly')
        self.depth_combobox.grid(row=1, column=1, sticky=tk.EW, pady=2)
        # 设置默认值
        default_depth = DEPTH_MAPPING.get(self.config['scan_depth'], '递归')
        self.depth_combobox.set(default_depth)

        # 哈希算法
        ttk.Label(config_frame, text='哈希算法:').grid(row=2, column=0, sticky=tk.W, pady=2)
        algorithm_values = list(ALGORITHM_MAPPING.values())
        self.algorithm_combobox = ttk.Combobox(config_frame, values=algorithm_values, state='readonly')
        self.algorithm_combobox.grid(row=2, column=1, sticky=tk.EW, pady=2)
        # 设置默认值
        default_algorithm = ALGORITHM_MAPPING.get(self.config['hash_algorithm'], 'MD5')
        self.algorithm_combobox.set(default_algorithm)

        # 排序依据
        ttk.Label(config_frame, text='排序依据:').grid(row=3, column=0, sticky=tk.W, pady=2)
        sort_values = list(SORT_MAPPING.values())
        self.sort_combobox = ttk.Combobox(config_frame, values=sort_values, state='readonly')
        self.sort_combobox.grid(row=3, column=1, sticky=tk.EW, pady=2)
        # 设置默认值
        default_sort = SORT_MAPPING.get(self.config['sort_by'], '修改时间')
        self.sort_combobox.set(default_sort)

        # 排序顺序
        ttk.Label(config_frame, text='排序顺序:').grid(row=4, column=0, sticky=tk.W, pady=2)
        order_values = list(ORDER_MAPPING.values())
        self.order_combobox = ttk.Combobox(config_frame, values=order_values, state='readonly')
        self.order_combobox.grid(row=4, column=1, sticky=tk.EW, pady=2)
        # 设置默认值
        default_order = ORDER_MAPPING.get(self.config['sort_order'], '降序')
        self.order_combobox.set(default_order)

        # 显示空文件
        ttk.Checkbutton(config_frame, text='显示空文件', variable=self.show_empty_var).grid(row=5, column=0, sticky=tk.W, pady=2)

        # 扫描模式选择（新增）
        scan_mode_frame = ttk.LabelFrame(config_frame, text='扫描模式', padding='5')
        scan_mode_frame.grid(row=6, column=0, columnspan=2, sticky=tk.EW, pady=5)
        
        # 使用Radiobutton实现互斥选择
        ttk.Radiobutton(scan_mode_frame, text='文件模式', 
                       variable=self.scan_mode_var, value='file',
                       command=self.on_scan_mode_changed).pack(side=tk.LEFT, padx=10)
        ttk.Radiobutton(scan_mode_frame, text='目录模式', 
                       variable=self.scan_mode_var, value='directory',
                       command=self.on_scan_mode_changed).pack(side=tk.LEFT, padx=10)
        ttk.Radiobutton(scan_mode_frame, text='空目录扫描', 
                       variable=self.scan_mode_var, value='empty_directory',
                       command=self.on_scan_mode_changed).pack(side=tk.LEFT, padx=10)

        # 比对选项
        compare_frame = ttk.LabelFrame(config_frame, text='比对选项', padding='5')
        compare_frame.grid(row=7, column=0, columnspan=2, sticky=tk.EW, pady=5)
        ttk.Checkbutton(compare_frame, text='仅比对文件名', variable=self.compare_filename_var).pack(side=tk.LEFT, padx=5)
        ttk.Checkbutton(compare_frame, text='比对文件大小', variable=self.compare_size_var).pack(side=tk.LEFT, padx=5)
        
        # 目录相似度比对控件（仅在目录模式显示）
        self.similarity_checkbox = ttk.Checkbutton(compare_frame, text='目录名称相似度', variable=self.use_similarity_var)
        self.similarity_checkbox.pack(side=tk.LEFT, padx=5)
        
        # 相似度阈值选择
        similarity_threshold_frame = ttk.Frame(compare_frame)
        similarity_threshold_frame.pack(side=tk.LEFT, padx=5)
        ttk.Label(similarity_threshold_frame, text='阈值:').pack(side=tk.LEFT)
        
        threshold_values = ['95%', '90%', '85%', '80%', '75%', '70%']
        self.similarity_threshold_combobox = ttk.Combobox(similarity_threshold_frame, 
                                                        textvariable=self.similarity_threshold_var,
                                                        values=threshold_values, 
                                                        state='readonly', width=6)
        self.similarity_threshold_combobox.pack(side=tk.LEFT, padx=2)

        # UI配置
        ui_frame = ttk.LabelFrame(config_frame, text='界面设置', padding='5')
        ui_frame.grid(row=8, column=0, columnspan=2, sticky=tk.EW, pady=5)
        
        # 字体大小选择
        ttk.Label(ui_frame, text='字体大小:').grid(row=0, column=0, sticky=tk.W, padx=5, pady=2)
        font_size_values = ['8', '9', '10', '11', '12', '14', '16', '18', '20', '22', '24']
        self.font_size_var = tk.IntVar(value=self.config.get('font_size', 10))
        self.font_size_combobox = ttk.Combobox(ui_frame, textvariable=self.font_size_var, values=font_size_values, state='readonly', width=5)
        self.font_size_combobox.grid(row=0, column=1, sticky=tk.W, padx=5, pady=2)
        
        # 应用字体大小按钮
        ttk.Button(ui_frame, text='应用字体大小', command=self.apply_font_size).grid(row=0, column=2, padx=5, pady=2)

        # 删除确认
        ttk.Checkbutton(config_frame, text='删除时需要确认', variable=self.delete_confirm_var).grid(row=5, column=1, sticky=tk.W, pady=2)

        # 按钮区域
        button_frame = ttk.Frame(main_frame)
        button_frame.pack(fill=tk.X, pady=5)

        # 左侧操作按钮
        left_buttons = ttk.Frame(button_frame)
        left_buttons.pack(side=tk.LEFT)
        self.scan_button = ttk.Button(left_buttons, text='扫描文件', command=self.scan_files)
        self.scan_button.pack(side=tk.LEFT, padx=5)
        self.stop_button = ttk.Button(left_buttons, text='停止扫描', command=self.stop_scan, state=tk.DISABLED)
        self.stop_button.pack(side=tk.LEFT, padx=5)
        ttk.Button(left_buttons, text='保存配置', command=self.save_config).pack(side=tk.LEFT, padx=5)
        ttk.Button(left_buttons, text='重置配置', command=self.reset_config).pack(side=tk.LEFT, padx=5)

        # 右侧选择和删除按钮
        right_buttons = ttk.Frame(button_frame)
        right_buttons.pack(side=tk.RIGHT)
        ttk.Button(right_buttons, text='智能选择', command=self.smart_select).pack(side=tk.LEFT, padx=2)
        ttk.Button(right_buttons, text='全选', command=self.select_all).pack(side=tk.LEFT, padx=2)
        ttk.Button(right_buttons, text='反选', command=self.invert_selection).pack(side=tk.LEFT, padx=2)
        ttk.Button(right_buttons, text='清除选择', command=self.deselect_all).pack(side=tk.LEFT, padx=2)
        ttk.Button(right_buttons, text='删除选中文件', command=self.delete_duplicates, style='Danger.TButton').pack(side=tk.LEFT, padx=5)

        # 结果显示区域
        result_frame = ttk.LabelFrame(main_frame, text='扫描结果', padding='10')
        result_frame.pack(fill=tk.BOTH, expand=True, pady=5)

        # 选择统计信息框架
        stats_frame = ttk.Frame(result_frame)
        stats_frame.pack(fill=tk.X, pady=(0, 5))
        
        self.stats_label = ttk.Label(stats_frame, text='未开始扫描', font=('Arial', 9))
        self.stats_label.pack(side=tk.LEFT)
        
        self.selection_stats_label = ttk.Label(stats_frame, text='', font=('Arial', 9), foreground='blue')
        self.selection_stats_label.pack(side=tk.RIGHT)

        # 创建树形视图 - 重新设计列结构
        columns = ('#1', '#2', '#3', '#4', '#5', '#6')
        self.result_tree = ttk.Treeview(result_frame, columns=columns, show='tree headings')
        self.result_tree.heading('#0', text='选择', anchor='center')
        self.result_tree.heading('#1', text='组#', anchor='center')
        self.result_tree.heading('#2', text='文件路径', anchor='w')
        self.result_tree.heading('#3', text='大小', anchor='e')
        self.result_tree.heading('#4', text='修改时间', anchor='center')
        self.result_tree.heading('#5', text='哈希值', anchor='center')
        self.result_tree.heading('#6', text='建议', anchor='center')

        self.result_tree.column('#0', width=60, anchor='center', minwidth=60)
        self.result_tree.column('#1', width=50, anchor='center', minwidth=50)
        self.result_tree.column('#2', width=300, stretch=True, minwidth=200)
        self.result_tree.column('#3', width=100, anchor='e', minwidth=80)
        self.result_tree.column('#4', width=140, anchor='center', minwidth=120)
        self.result_tree.column('#5', width=100, anchor='center', minwidth=80)
        self.result_tree.column('#6', width=80, anchor='center', minwidth=60)

        # 添加滚动条
        y_scroll = ttk.Scrollbar(result_frame, orient=tk.VERTICAL, command=self.result_tree.yview)
        x_scroll = ttk.Scrollbar(result_frame, orient=tk.HORIZONTAL, command=self.result_tree.xview)
        self.result_tree.configure(yscrollcommand=y_scroll.set, xscrollcommand=x_scroll.set)

        y_scroll.pack(side=tk.RIGHT, fill=tk.Y)
        x_scroll.pack(side=tk.BOTTOM, fill=tk.X)
        self.result_tree.pack(fill=tk.BOTH, expand=True)

        # 绑定事件
        self.result_tree.bind('<Button-1>', self.on_tree_click)
        self.result_tree.bind('<Double-Button-1>', self.on_tree_double_click)
        self.result_tree.bind('<Button-3>', self.on_tree_right_click)  # 右键点击

        # 初始化目录列表
        self._update_directory_listbox()

        # 状态栏
        status_frame = ttk.Frame(self.root)
        status_frame.pack(side=tk.BOTTOM, fill=tk.X, padx=2, pady=2)
        
        # 主状态标签
        self.status_var = tk.StringVar(value='就绪')
        self.main_status_label = ttk.Label(status_frame, textvariable=self.status_var, relief=tk.SUNKEN, anchor=tk.W)
        self.main_status_label.pack(side=tk.LEFT, fill=tk.X, expand=True)
        
        # 分隔符
        ttk.Separator(status_frame, orient=tk.VERTICAL).pack(side=tk.LEFT, fill=tk.Y, padx=2)
        
        # 进度状态标签
        self.progress_var = tk.StringVar(value='')
        self.progress_label = ttk.Label(status_frame, textvariable=self.progress_var, relief=tk.SUNKEN, anchor=tk.CENTER, width=30)
        self.progress_label.pack(side=tk.LEFT, padx=2)
        
        # 分隔符
        ttk.Separator(status_frame, orient=tk.VERTICAL).pack(side=tk.LEFT, fill=tk.Y, padx=2)
        
        # 时间状态标签
        self.time_var = tk.StringVar(value='')
        self.time_label = ttk.Label(status_frame, textvariable=self.time_var, relief=tk.SUNKEN, anchor=tk.CENTER, width=20)
        self.time_label.pack(side=tk.LEFT, padx=2)

        # 初始化界面状态
        self.on_scan_mode_changed()
        
        # 标记窗口已初始化
        self._window_initialized = True

    def _update_directory_listbox(self):
        # 清空列表
        self.dir_listbox.delete(0, tk.END)
        # 添加目录
        for directory in self.directory_list:
            self.dir_listbox.insert(tk.END, directory)

    def add_directory(self):
        directory = filedialog.askdirectory()
        if directory and directory not in self.directory_list:
            self.directory_list.append(directory)
            self._update_directory_listbox()
            logger.info(f'添加目录: {directory}')

    def remove_directory(self):
        selected_indices = self.dir_listbox.curselection()
        if selected_indices:
            # 从后往前删除，避免索引变化问题
            for i in sorted(selected_indices, reverse=True):
                removed_dir = self.directory_list.pop(i)
                logger.info(f'删除目录: {removed_dir}')
            self._update_directory_listbox()
        else:
            messagebox.showinfo('提示', '请先选择要删除的目录')

    def on_scan_mode_changed(self):
        """处理扫描模式变化时的界面联动"""
        self.update_scan_mode()
    def stop_scan(self):
        """停止正在进行的扫描操作"""
        self.stop_scan_flag = True
        self.status_var.set('扫描已停止')
        logger.info('扫描已被用户停止')
        # 更新按钮状态
        self.scan_button.config(state=tk.NORMAL)
        self.stop_button.config(state=tk.DISABLED)
        
    def update_scan_mode(self):
        scan_mode = self.scan_mode_var.get()
        if scan_mode == 'file':
            # 文件模式：启用哈希算法和比对选项
            self.algorithm_combobox.config(state='readonly')
            # 保持比对选项可用（在compare_frame中）
            self.scan_button.config(text='扫描文件')
            # 隐藏相似度比对控件
            self._show_similarity_controls(False)
        elif scan_mode == 'directory':
            # 目录模式：禁用哈希算法，根据规范不使用哈希比对
            self.algorithm_combobox.config(state='disabled')
            self.scan_button.config(text='扫描目录')
            # 显示相似度比对控件
            self._show_similarity_controls(True)
            
        elif scan_mode == 'empty_directory':
            # 空目录扫描：禁用哈希算法和比对选项
            self.algorithm_combobox.config(state='disabled')
            self.scan_button.config(text='扫描空目录')
            # 隐藏相似度比对控件
            self._show_similarity_controls(False)
            
        # 同步更新旧的scan_directory_mode_var以保持向后兼容
        self.scan_directory_mode_var.set(scan_mode == 'directory')
        
    def _show_similarity_controls(self, show: bool):
        """显示或隐藏相似度比对控件"""
        if show:
            self.similarity_checkbox.pack(side=tk.LEFT, padx=5)
            self.similarity_threshold_combobox.master.pack(side=tk.LEFT, padx=5)
        else:
            self.similarity_checkbox.pack_forget()
            self.similarity_threshold_combobox.master.pack_forget()

    def on_tree_click(self, event):
        """处理鼠标点击事件"""
        item = self.result_tree.identify_row(event.y)
        if not item:
            return
        
        column = self.result_tree.identify_column(event.x)
        # 只处理复选框列（第一列）的点击
        if column == '#0':
            self.toggle_item_check(item)
            return 'break'
    
    def on_tree_double_click(self, event):
        """处理双击事件 - 打开文件位置"""
        item = self.result_tree.identify_row(event.y)
        if not item:
            return
            
        # 获取文件路径
        values = self.result_tree.item(item, 'values')
        if len(values) >= 2:
            file_path = values[1]  # 文件路径在第二列
            if os.path.exists(file_path):
                # 在文件管理器中显示文件
                self._open_file_location(file_path)
    
    def on_tree_right_click(self, event):
        """处理右键点击事件 - 显示上下文菜单"""
        item = self.result_tree.identify_row(event.y)
        if not item:
            return
            
        # 创建上下文菜单
        context_menu = tk.Menu(self.root, tearoff=0)
        
        # 检查是否是组头
        if item in self.group_headers.values():
            group_index = self._get_group_index_by_header(item)
            if group_index is not None:
                context_menu.add_command(label='选择此组全部', command=lambda: self.select_group(group_index))
                context_menu.add_command(label='取消选择此组', command=lambda: self.deselect_group(group_index))
                context_menu.add_separator()
                context_menu.add_command(label='智能选择此组', command=lambda: self.smart_select_group(group_index))
        else:
            # 单个文件的右键菜单
            context_menu.add_command(label='切换选择状态', command=lambda: self.toggle_item_check(item))
            context_menu.add_separator()
            values = self.result_tree.item(item, 'values')
            if len(values) >= 2:
                file_path = values[1]
                context_menu.add_command(label='在文件管理器中显示', 
                                       command=lambda: self._open_file_location(file_path))
        
        context_menu.add_separator()
        context_menu.add_command(label='取消', command=lambda: context_menu.destroy())
        
        # 显示菜单
        try:
            context_menu.tk_popup(event.x_root, event.y_root)
        finally:
            context_menu.grab_release()
    
    def _open_file_location(self, file_path):
        """在文件管理器中显示文件"""
        # 确保路径使用Windows标准格式
        normalized_path = os.path.normpath(file_path)
        
        if os.path.exists(normalized_path):
            import subprocess
            try:
                # 使用subprocess.Popen异步启动，避免等待进程完成
                # 方法1: 尝试使用explorer /select命令选中文件
                subprocess.Popen(['explorer', '/select,', normalized_path])
                logger.info(f'已在文件管理器中显示文件: {normalized_path}')
            except Exception as e:
                logger.warning(f'使用explorer /select失败: {str(e)}')
                try:
                    # 备用方法: 打开文件所在目录
                    dir_path = os.path.dirname(normalized_path)
                    os.startfile(dir_path)
                    logger.info(f'已打开文件目录: {dir_path}')
                except Exception as e2:
                    logger.error(f'无法打开文件位置: {str(e2)}')
                    messagebox.showwarning('警告', f'无法打开文件位置: {str(e2)}')
        else:
            logger.warning(f'文件不存在: {file_path}')
            messagebox.showwarning('警告', f'文件不存在: {file_path}')
    
    def _get_group_index_by_header(self, header_item):
        """根据组头项获取组索引"""
        for group_index, item_id in self.group_headers.items():
            if item_id == header_item:
                return group_index
        return None

    def select_all(self):
        """全选所有文件项（不包括组头）"""
        self.checked_items.clear()
        
        for item in self.result_tree.get_children():
            # 跳过组头
            if item in self.group_headers.values():
                continue
                
            self.checked_items[item] = True
            current_tags = self.result_tree.item(item, 'tags')
            new_tags = [tag for tag in current_tags if tag != 'unchecked']
            if 'checked' not in new_tags:
                new_tags.append('checked')
            self.result_tree.item(item, text='☑', tags=new_tags)
        
        # 更新所有组头状态
        for group_index in self.group_headers.keys():
            self._update_group_header_state(group_index)
        
        self._update_selection_stats()

    def deselect_all(self):
        """取消全选所有文件项"""
        self.checked_items.clear()
        
        for item in self.result_tree.get_children():
            # 跳过组头
            if item in self.group_headers.values():
                continue
                
            current_tags = self.result_tree.item(item, 'tags')
            new_tags = [tag for tag in current_tags if tag != 'checked']
            if 'unchecked' not in new_tags:
                new_tags.append('unchecked')
            self.result_tree.item(item, text='☐', tags=new_tags)
        
        # 更新所有组头状态
        for group_index in self.group_headers.keys():
            self._update_group_header_state(group_index)
        
        self._update_selection_stats()

    def invert_selection(self):
        """反选所有文件项"""
        for item in self.result_tree.get_children():
            # 跳过组头
            if item in self.group_headers.values():
                continue
                
            current_tags = self.result_tree.item(item, 'tags')
            if item in self.checked_items:
                del self.checked_items[item]
                new_tags = [tag for tag in current_tags if tag != 'checked']
                if 'unchecked' not in new_tags:
                    new_tags.append('unchecked')
                self.result_tree.item(item, text='☐', tags=new_tags)
            else:
                self.checked_items[item] = True
                new_tags = [tag for tag in current_tags if tag != 'unchecked']
                if 'checked' not in new_tags:
                    new_tags.append('checked')
                self.result_tree.item(item, text='☑', tags=new_tags)
        
        # 更新所有组头状态
        for group_index in self.group_headers.keys():
            self._update_group_header_state(group_index)
        
        self._update_selection_stats()



    def get_selected_files(self):
        """获取选中的文件路径"""
        selected_files = []
        for item in self.checked_items:
            # 跳过组头
            if item in self.group_headers.values():
                continue
            file_path = self.result_tree.set(item, '#2')  # 从存储的完整路径获取
            if file_path:
                selected_files.append(file_path)
        return selected_files

    def scan_files(self):
        # 重置停止标志
        self.stop_scan_flag = False
        
        # 更新按钮状态
        self.scan_button.config(state=tk.DISABLED)
        self.stop_button.config(state=tk.NORMAL)
        
        # 清除之前的结果
        for item in self.result_tree.get_children():
            self.result_tree.delete(item)
        
        # 重置所有状态变量
        self.duplicate_groups = []
        self.checked_items.clear()
        self.group_tags = []
        self.group_headers = {}
        self.item_to_group = {}
        self.group_checkboxes = {}
        
        # 重置统计信息
        self.stats_label.config(text='正在扫描...')
        self.selection_stats_label.config(text='')

        # 获取配置
        scan_depth = REVERSE_DEPTH_MAPPING.get(self.depth_combobox.get(), 'recursive')
        if scan_depth not in ['recursive', 'shallow']:
            scan_depth = int(scan_depth)
        hash_algorithm = REVERSE_ALGORITHM_MAPPING.get(self.algorithm_combobox.get(), 'md5')
        sort_by = REVERSE_SORT_MAPPING.get(self.sort_combobox.get(), 'modified_time')
        sort_order = REVERSE_ORDER_MAPPING.get(self.order_combobox.get(), 'descending')
        show_empty_files = self.show_empty_var.get()
        
        # 获取扫描模式
        scan_mode = self.scan_mode_var.get()

        # 检查目录是否存在
        if not self.directory_list:
            messagebox.showerror('错误', '请添加至少一个扫描目录')
            return

        for directory in self.directory_list:
            if not os.path.isdir(directory):
                messagebox.showerror('错误', f'目录不存在: {directory}')
                return

        # 更新状态栏
        self.status_var.set('正在扫描...')
        self.root.update_idletasks()

        try:
            # 创建扫描器
            scanner = Scanner()
            
            # 根据扫描模式执行不同的扫描逻辑
            if scan_mode == 'empty_directory':
                # 空目录扫描模式
                self._scan_empty_directories(scanner, scan_depth)
            else:
                # 文件模式或目录模式
                self._scan_files_or_directories(scanner, scan_mode, scan_depth, hash_algorithm, 
                                               sort_by, sort_order, show_empty_files)
                
        except Exception as e:
            self.status_var.set(f'扫描出错: {str(e)}')
            logger.error(f'扫描出错: {str(e)}')
            messagebox.showerror('错误', f'扫描出错: {str(e)}')
            
    def _scan_empty_directories(self, scanner, scan_depth):
        """空目录扫描逻辑"""
        logger.info(f'开始空目录扫描: {self.directory_list}')
        
        # 确定扫描深度
        if scan_depth == 'recursive':
            recursive = True
            max_depth_param = None
        elif scan_depth == 'shallow':
            recursive = False
            max_depth_param = None
        elif isinstance(scan_depth, int) and scan_depth >= 0:
            recursive = scan_depth > 0
            max_depth_param = scan_depth if scan_depth > 0 else None
            if scan_depth > 0:
                logger.info(f'使用深度限制递归扫描: {scan_depth} 层')
            else:
                logger.info('使用浅层扫描（仅当前目录）')
        else:
            recursive = True
            max_depth_param = None
            logger.warning(f'无效的扫描深度: {scan_depth}，使用recursive')

        # 执行空目录扫描
        empty_dirs = scanner.scan_empty_directories(
            self.directory_list,
            recursive=recursive,
            max_depth=max_depth_param,
            stop_event=lambda: self.stop_scan_flag
        )

        # 检查是否停止
        if self.stop_scan_flag:
            self.status_var.set('扫描已停止')
            self.scan_button.config(state=tk.NORMAL)
            self.stop_button.config(state=tk.DISABLED)
            return

        if not empty_dirs:
            self.status_var.set('未找到空目录')
            messagebox.showinfo('信息', '未找到空目录')
            self.scan_button.config(state=tk.NORMAL)
            self.stop_button.config(state=tk.DISABLED)
            return

        self.status_var.set(f'找到 {len(empty_dirs)} 个空目录')
        logger.info(f'找到 {len(empty_dirs)} 个空目录')
        self.scan_button.config(state=tk.NORMAL)
        self.stop_button.config(state=tk.DISABLED)
        
        # 为空目录显示创建一个整组
        self.duplicate_groups = [empty_dirs]  # 将所有空目录作为一个组
        
        # 显示结果到树形视图
        self._display_empty_directories_results(empty_dirs)
        
        # 更新统计信息
        self.stats_label.config(text=f'共找到 {len(empty_dirs)} 个空目录')
        self._update_selection_stats()
        
    def _display_empty_directories_results(self, empty_dirs):
        """显示空目录扫描结果"""
        self.group_tags = []
        self.group_headers = {}
        self.item_to_group = {}
        self.group_checkboxes = {}
        
        # 配置样式
        self.result_tree.tag_configure('checked', background='#e6f7ff', foreground='#0066cc')
        self.result_tree.tag_configure('unchecked', background='', foreground='')
        # 使用全局字体设置，只指定样式为粗体
        default_font = font.nametofont("TkDefaultFont")
        bold_font = font.Font(family=default_font.actual()['family'],
                                 size=default_font.actual()['size'],
                                 weight='bold')
        self.result_tree.tag_configure('group_header', background='#d4edda', foreground='#155724', font=bold_font)
        
        # 创建组头
        group_name = f'空目录列表 ({len(empty_dirs)} 个目录)'
        header_item = self.result_tree.insert('', tk.END, text='☐', 
                                             values=('组1', group_name, '', '', '', ''),
                                             tags=('group_header',), open=True)
        self.group_headers[0] = header_item
        self.group_checkboxes[header_item] = 'none'
        
        # 添加空目录项目
        group_items = []
        for dir_index, dir_info in enumerate(empty_dirs):
            dir_path = dir_info['path']
            modified_time = dir_info.get('modified_time', 'N/A')
            
            # 格式化修改时间
            if isinstance(modified_time, (int, float)):
                import datetime
                modified_time = datetime.datetime.fromtimestamp(modified_time).strftime('%Y-%m-%d %H:%M:%S')
            
            # 空目录不需要建议操作，可以直接删除
            suggestion = '可删除'
            
            # 设置行颜色
            tag = 'empty_dir'
            self.result_tree.tag_configure(tag, background='#fff3cd')

            # 格式化目录大小（空目录大小为0）
            readable_size = '0 B'
            
            # 插入目录项
            item = self.result_tree.insert(header_item, tk.END, text='☐',
                                          values=('', os.path.basename(dir_path), readable_size, 
                                                 modified_time, '-', suggestion),
                                          tags=(tag, 'unchecked'))
            
            # 设置完整路径为tooltip（暂时存储在values中）
            # 确保路径使用Windows标准格式
            normalized_dir_path = os.path.normpath(dir_path)
            self.result_tree.set(item, '#2', normalized_dir_path)  # 存储标准化的完整路径
            
            group_items.append(item)
            self.item_to_group[item] = 0  # 所有空目录都属于组0
        
        self.group_tags.append(group_items)
        
    def _scan_files_or_directories(self, scanner, scan_mode, scan_depth, hash_algorithm, 
                                   sort_by, sort_order, show_empty_files):
        """文件模式或目录模式的扫描逻辑"""
        from duplicate_detector.duplicate_detector import DuplicateDetector
        detector = DuplicateDetector()
        
        # 记录开始时间
        import time
        scan_start_time = time.time()
        
        logger.info(f'开始扫描目录: {self.directory_list}')
        
        # 确定扫描深度
        if scan_depth == 'recursive':
            recursive = True
            max_depth_param = None
        elif scan_depth == 'shallow':
            recursive = False
            max_depth_param = None
        elif isinstance(scan_depth, int) and scan_depth >= 0:
            recursive = scan_depth > 0
            max_depth_param = scan_depth if scan_depth > 0 else None
            if scan_depth > 0:
                logger.info(f'使用深度限制递归扫描: {scan_depth} 层')
            else:
                logger.info('使用浅层扫描（仅当前目录）')
        else:
            recursive = True
            max_depth_param = None
            logger.warning(f'无效的扫描深度: {scan_depth}，使用recursive')

        # 扫描文件或目录
        scan_directory_mode = (scan_mode == 'directory')
        
        # 更新状态显示
        scan_type = '目录' if scan_directory_mode else '文件'
        self.status_var.set(f'正在扫描{scan_type}...')
        self.progress_var.set('')
        self.time_var.set('')
        self.root.update_idletasks()
        
        # 使用修改后的scanner进行扫描
        file_info_list = scanner.start_scan(
            self.directory_list,
            recursive=recursive,
            scan_dirs_as_files=scan_directory_mode,  # 目录模式时扫描目录
            max_depth=max_depth_param  # 传递深度参数
        )

        if not file_info_list:
            status_text = '未找到目录' if scan_directory_mode else '未找到文件'
            self.status_var.set(status_text)
            self.progress_var.set('')
            self.time_var.set('')
            messagebox.showinfo('信息', status_text)
            return

        if scan_directory_mode:
            # 目录模式：直接进行重复检测，不计算哈希值
            for file_info in file_info_list:
                detector.add_file(file_info)
        else:
            # 文件模式：创建哈希计算器并计算哈希值
            from hash_calculator.hash_calculator import HashCalculator
            hash_calculator = HashCalculator(algorithm=hash_algorithm)

            # 计算每个文件的哈希值
            self.status_var.set(f'正在计算哈希值 ({len(file_info_list)} 个文件)...')
            self.root.update_idletasks()

            for i, file_info in enumerate(file_info_list):
                # 检查是否停止
                if self.stop_scan_flag:
                    return

                # 更新状态栏
                self.status_var.set(f'正在计算哈希值 ({i+1}/{len(file_info_list)})...')
                self.progress_var.set(f'{(i+1)/len(file_info_list)*100:.1f}%')
                
                # 计算耗时
                elapsed = time.time() - scan_start_time
                self.time_var.set(f'耗时: {elapsed:.1f}s')
                self.root.update_idletasks()

                # 计算哈希值
                hash_value = hash_calculator.compute_file_hash(file_info['path'])
                file_info['hash'] = hash_value

                # 如果是显示空文件且文件大小为0，则添加到检测器
                if show_empty_files or file_info['size'] > 0:
                    detector.add_file(file_info)

        # 检查是否停止
        if self.stop_scan_flag:
            self.status_var.set('扫描已停止')
            self.progress_var.set('')
            self.time_var.set('')
            self.scan_button.config(state=tk.NORMAL)
            self.stop_button.config(state=tk.DISABLED)
            return

        # 查找重复文件或目录
        status_text = '正在查找重复目录...' if scan_directory_mode else '正在查找重复文件...'
        self.status_var.set(status_text)
        self.progress_var.set('处理中...')
        self.root.update_idletasks()

        # 设置比对选项
        compare_filename = self.compare_filename_var.get()
        compare_size = self.compare_size_var.get()
        
        # 获取相似度比对设置（仅在目录模式下有效）
        use_similarity = scan_directory_mode and self.use_similarity_var.get()
        similarity_threshold = 95.0  # 默认值
        if use_similarity:
            try:
                # 从字符串中提取数值，去除%号
                threshold_str = self.similarity_threshold_var.get().rstrip('%')
                similarity_threshold = float(threshold_str)
                logger.info(f'启用相似度比对，阈值: {similarity_threshold}%')
            except (ValueError, AttributeError):
                logger.warning(f'无效的相似度阈值: {self.similarity_threshold_var.get()}，使用默认值 95%')
                similarity_threshold = 95.0

        # 相似度进度回调函数
        def similarity_progress(current_pair, total_pairs, current_dirs=None, total_dirs=None):
            if self.stop_scan_flag:
                return
            
            # 更新主状态
            if use_similarity:
                self.status_var.set(f'正在进行目录相似度比对...')
            
            # 更新进度显示
            if total_pairs > 0:
                percent = (current_pair / total_pairs * 100) if total_pairs > 0 else 0
                status_text = f'已比对: {current_pair}/{total_pairs} 对目录 ({percent:.1f}%)'
                if current_dirs is not None and total_dirs is not None:
                    status_text += f' (已处理: {current_dirs}/{total_dirs} 个目录)'
                self.progress_var.set(status_text)
            
            # 更新耗时
            elapsed = time.time() - scan_start_time
            self.time_var.set(f'耗时: {elapsed:.1f}s')
            
            # 估算剩余时间
            if current_pair > 0 and total_pairs > 0:
                percent = current_pair / total_pairs * 100
                if percent > 0:
                    estimated_total = elapsed / (percent / 100)
                    remaining = estimated_total - elapsed
                    if remaining > 0:
                        self.time_var.set(f'耗时: {elapsed:.1f}s / 估计: {remaining:.1f}s')
            
            self.root.update_idletasks()
        
        # 基础进度回调函数
        def detection_progress(processed, total):
            if self.stop_scan_flag:
                return
            
            percent = (processed / total * 100) if total > 0 else 0
            
            # 根据模式显示不同的进度信息
            if use_similarity:
                # 相似度模式下使用更详细的进度显示
                similarity_progress(processed, total)
            else:
                # 普通模式下的进度显示
                self.progress_var.set(f'{processed}/{total} ({percent:.1f}%)')
                
                # 更新耗时
                elapsed = time.time() - scan_start_time
                self.time_var.set(f'耗时: {elapsed:.1f}s')
                
                # 估算剩余时间
                if processed > 0 and percent > 0:
                    estimated_total = elapsed / (percent / 100)
                    remaining = estimated_total - elapsed
                    if remaining > 0:
                        self.time_var.set(f'耗时: {elapsed:.1f}s / 估计: {remaining:.1f}s')
            
            self.root.update_idletasks()

        # 清空相似度计算缓存（如果有）
        if use_similarity:
            try:
                from string_similarity import clear_similarity_cache
                clear_similarity_cache()
                logger.info('已清空相似度计算缓存')
            except:
                pass
        
        # 调用检测器查找重复项
        self.duplicate_groups = detector.find_duplicates(
            compare_filename=compare_filename,
            compare_size=compare_size,
            directory_mode=scan_directory_mode,  # 传递目录模式标志
            stop_event=lambda: self.stop_scan_flag,
            use_similarity=use_similarity,
            similarity_threshold=similarity_threshold,
            progress_callback=detection_progress  # 传递进度回调
        )

        # 检查是否停止
        if self.stop_scan_flag:
            self.status_var.set('扫描已停止')
            self.progress_var.set('')
            self.time_var.set('')
            self.scan_button.config(state=tk.NORMAL)
            self.stop_button.config(state=tk.DISABLED)
            return

        # 显示结果
        if not self.duplicate_groups:
            result_text = '未找到重复目录' if scan_directory_mode else '未找到重复文件'
            
            # 计算总耗时
            total_elapsed = time.time() - scan_start_time
            
            self.status_var.set(result_text)
            self.progress_var.set('完成')
            self.time_var.set(f'总耗时: {total_elapsed:.1f}s')
            
            messagebox.showinfo('信息', result_text)
            self.scan_button.config(state=tk.NORMAL)
            self.stop_button.config(state=tk.DISABLED)
            return

        item_type = '目录' if scan_directory_mode else '文件'
        
        # 计算总耗时
        total_elapsed = time.time() - scan_start_time
        
        self.status_var.set(f'找到 {len(self.duplicate_groups)} 组重复{item_type}')
        self.progress_var.set('完成')
        self.time_var.set(f'总耗时: {total_elapsed:.1f}s')
        
        logger.info(f'找到 {len(self.duplicate_groups)} 组重复{item_type}')

        # 排序重复文件组
        for i in range(len(self.duplicate_groups)):
            detector.sort_duplicates(i, sort_by=sort_by, reverse=(sort_order == 'descending'))

        # 显示结果到树形视图
        self._display_duplicate_results(scan_directory_mode)
        
        # 执行智能选择
        self.smart_select()
        
        # 更新统计信息
        self._update_selection_stats()
    
    def apply_font_size(self):
        """应用字体大小设置"""
        try:
            font_size = self.font_size_var.get()
            # 确保字体大小在合理范围内
            if 8 <= font_size <= 24:
                self.set_global_font(font_size)
                # 保存字体大小配置
                self.config_manager.set('font_size', font_size)
                self.status_var.set(f'字体大小已设置为 {font_size}')
            else:
                messagebox.showwarning('警告', '字体大小应在8-24之间')
        except (ValueError, TypeError):
            messagebox.showerror('错误', '无效的字体大小')
        
    def _display_duplicate_results(self, scan_directory_mode):
        """显示重复文件或目录的扫描结果"""
        self.group_tags = []
        self.group_headers = {}
        self.item_to_group = {}
        self.group_checkboxes = {}
        
        # 配置样式
        self.result_tree.tag_configure('checked', background='#e6f7ff', foreground='#0066cc')
        self.result_tree.tag_configure('unchecked', background='', foreground='')
        self.result_tree.tag_configure('group_header', background='#d4edda', foreground='#155724', font=('Arial', 9, 'bold'))
        
        for group_index, group in enumerate(self.duplicate_groups):
            # 创建组头
            item_type = '目录' if scan_directory_mode else '文件'
            group_name = f'重复组 {group_index + 1} ({len(group)} 个{item_type})'
            header_item = self.result_tree.insert('', tk.END, text='☐', 
                                                 values=(f'组{group_index + 1}', group_name, '', '', '', ''),
                                                 tags=('group_header',), open=True)
            self.group_headers[group_index] = header_item
            self.group_checkboxes[header_item] = 'none'
            
            # 添加组内文件
            group_items = []
            for file_index, file_info in enumerate(group):
                file_path = file_info['path']
                size = file_info['size']
                modified_time = file_info.get('modified_time', 'N/A')
                
                # 目录模式下不显示哈希值
                if scan_directory_mode:
                    hash_value = '-'  # 目录模式不显示哈希值
                else:
                    hash_value = file_info.get('hash', 'N/A')
                
                # 格式化修改时间
                if isinstance(modified_time, (int, float)):
                    import datetime
                    modified_time = datetime.datetime.fromtimestamp(modified_time).strftime('%Y-%m-%d %H:%M:%S')
                
                # 生成建议
                suggestion = '保留' if file_index == 0 else '删除'
                if file_index == 0:
                    # 检查是否是最新文件
                    newest = max(group, key=lambda x: x.get('modified_time', 0))
                    if file_info == newest:
                        suggestion = '保留(最新)'
                
                # 设置行颜色，同一组文件使用相同颜色
                tag = f'group_{group_index + 1}'
                if group_index % 2 == 0:
                    self.result_tree.tag_configure(tag, background='#f8f9fa')
                else:
                    self.result_tree.tag_configure(tag, background='#ffffff')

                # 格式化文件大小
                readable_size = self.format_size(size)
                
                # 插入文件项
                item = self.result_tree.insert(header_item, tk.END, text='☐',
                                              values=('', os.path.basename(file_path), readable_size, 
                                                     modified_time, hash_value[:8] + '...' if len(hash_value) > 8 else hash_value, suggestion),
                                              tags=(tag, 'unchecked'))
                
                # 设置完整路径为tooltip（暂时存储在values中）
                # 确保路径使用Windows标准格式
                normalized_file_path = os.path.normpath(file_path)
                self.result_tree.set(item, '#2', normalized_file_path)  # 存储标准化的完整路径
                
                group_items.append(item)
                self.item_to_group[item] = group_index
            
            self.group_tags.append(group_items)

    def delete_duplicates(self):
        if not self.checked_items:
            messagebox.showinfo('信息', '请先选择要删除的文件')
            return

        delete_confirm = self.delete_confirm_var.get()

        if delete_confirm:
            result = messagebox.askyesno('确认', '确定要删除选中的文件或目录吗？')
            if not result:
                return

        try:
            deleted_count = 0
            selected_files = self.get_selected_files()
            
            # 获取当前扫描模式
            scan_mode = self.scan_mode_var.get()
            is_empty_directory_mode = (scan_mode == 'empty_directory')

            # 删除选中的文件或目录
            for file_path in selected_files:
                try:
                    if os.path.exists(file_path):
                        if is_empty_directory_mode and os.path.isdir(file_path):
                            # 空目录模式：删除目录
                            os.rmdir(file_path)
                            deleted_count += 1
                            logger.info(f'成功删除空目录: {file_path}')
                        elif os.path.isfile(file_path):
                            # 文件模式：删除文件
                            os.remove(file_path)
                            deleted_count += 1
                            logger.info(f'成功删除文件: {file_path}')
                        else:
                            logger.warning(f'路径不是文件也不是目录: {file_path}')
                    else:
                        logger.warning(f'文件或目录不存在: {file_path}')
                except Exception as e:
                    logger.error(f'删除文件时出错: {file_path}, 错误: {str(e)}')

            # 更新界面：移除已删除的项目从树控件
            items_to_remove = []
            for item_id in list(self.checked_items.keys()):
                file_path = self.result_tree.set(item_id, '#2')
                if file_path in selected_files:
                    items_to_remove.append(item_id)
            
            # 从树控件中移除已删除的项目
            for item_id in items_to_remove:
                self.result_tree.delete(item_id)
                if item_id in self.checked_items:
                    del self.checked_items[item_id]
                # 从组映射中移除
                if item_id in self.item_to_group:
                    group_index = self.item_to_group[item_id]
                    if group_index in self.group_tags and item_id in self.group_tags[group_index]:
                        self.group_tags[group_index].remove(item_id)
                    del self.item_to_group[item_id]

            # 更新组头状态
            for group_index in range(len(self.group_tags)):
                self._update_group_header_state(group_index)

            # 更新统计信息
            self._update_selection_stats()
            
            # 更新状态栏
            self.status_var.set(f'已删除 {deleted_count} 个项目')
            logger.info(f'已删除 {deleted_count} 个项目')
            messagebox.showinfo('信息', f'已删除 {deleted_count} 个项目')
            
            # 不自动重新扫描，避免无限循环
            
        except Exception as e:
            self.status_var.set(f'删除出错: {str(e)}')
            logger.error(f'删除出错: {str(e)}')
            messagebox.showerror('错误', f'删除出错: {str(e)}')

    def format_size(self, size_bytes):
        # 将字节数转换为人类可读的格式
        if size_bytes < 0:
            return '未知'
        elif size_bytes < 1024:
            return f'{size_bytes} B'
        elif size_bytes < 1024 * 1024:
            return f'{size_bytes / 1024:.2f} KB'
        elif size_bytes < 1024 * 1024 * 1024:
            return f'{size_bytes / (1024 * 1024):.2f} MB'
        else:
            return f'{size_bytes / (1024 * 1024 * 1024):.2f} GB'

    def _load_window_config(self):
        """加载窗口配置"""
        # 加载窗口大小
        try:
            width = 1024
            height = 768
            
            if 'window_size' in self.config:
                # 确保width和height是整数（支持字典格式）
                if isinstance(self.config['window_size'], dict):
                    width = int(self.config['window_size'].get('width', 1024))
                    height = int(self.config['window_size'].get('height', 768))
                elif isinstance(self.config['window_size'], (list, tuple)) and len(self.config['window_size']) >= 2:
                    # 兼容旧格式
                    width = int(self.config['window_size'][0])
                    height = int(self.config['window_size'][1])
                else:
                    logger.warning(f'窗口大小配置格式无效: {self.config["window_size"]}，使用默认值')
            
            # 确保大小在合理范围内
            min_width = 800
            min_height = 600
            if min_width <= width <= 3000 and min_height <= height <= 2000:
                logger.info(f'加载窗口配置: {width}x{height}')
                self.root.geometry(f'{width}x{height}')
            else:
                logger.warning(f'窗口大小 {width}x{height} 超出合理范围，使用默认值 {min_width}x{min_height}')
                self.root.geometry(f'{min_width}x{min_height}')
        except Exception as e:
            logger.error(f'加载窗口配置出错: {str(e)}，使用默认值 1024x768')
            self.root.geometry('1024x768')

        # 加载窗口位置
        
        # 加载窗口位置
        if 'window_position' in self.config:
            try:
                # 确保x和y是整数（支持字典格式）
                if isinstance(self.config['window_position'], dict):
                    x = int(self.config['window_position'].get('x', 0))
                    y = int(self.config['window_position'].get('y', 0))
                else:
                    # 兼容旧格式
                    x = int(self.config['window_position'][0]) if isinstance(self.config['window_position'], (list, tuple)) else 0
                    y = int(self.config['window_position'][1]) if isinstance(self.config['window_position'], (list, tuple)) else 0
                
                # 检查位置是否有效（在屏幕范围内）
                screen_width = self.root.winfo_screenwidth()
                screen_height = self.root.winfo_screenheight()
                
                # 尝试获取当前窗口大小
                current_geometry = self.root.geometry().split('+')[0].split('x')
                width = int(current_geometry[0]) if len(current_geometry) >= 1 else 800
                height = int(current_geometry[1]) if len(current_geometry) >= 2 else 600
                
                # 确保窗口在屏幕内
                x = max(0, min(x, screen_width - 100))
                y = max(0, min(y, screen_height - 100))
                
                self.root.geometry(f'{width}x{height}+{x}+{y}')
            except (ValueError, TypeError, IndexError):
                # 如果位置无效，不做任何操作，保持当前大小
                pass
        
        # 加载字体大小
        if 'font_size' in self.config:
            try:
                font_size = int(self.config['font_size'])
                # 确保字体大小在合理范围内
                if 8 <= font_size <= 24:
                    self.set_global_font(font_size)
            except (ValueError, TypeError):
                self.set_global_font(10)  # 默认字体大小

    def set_global_font(self, font_size):
        """设置全局字体大小"""
        # 创建基础字体配置
        default_font = font.nametofont("TkDefaultFont")
        text_font = font.nametofont("TkTextFont")
        fixed_font = font.nametofont("TkFixedFont")
        
        # 更新字体大小
        default_font.configure(size=font_size)
        text_font.configure(size=font_size)
        fixed_font.configure(size=font_size)
        
        # 应用字体配置到所有组件
        self.root.option_add("*Font", default_font)
        
        # 特别处理结果树的字体
        if hasattr(self, 'result_tree'):
            # 更新结果树的标签配置
            bold_font = font.Font(family=default_font.actual()['family'],
                                     size=default_font.actual()['size'],
                                     weight='bold')
            self.result_tree.tag_configure('group_header', font=bold_font)
            
        # 特别处理扫描模式区域的字体 - 只处理已存在的属性
        for attr_name in ['scan_mode_var', 'compare_filename_var', 'compare_size_var', 'delete_confirm_var']:
            if hasattr(self, attr_name):
                widget = getattr(self, attr_name)
                try:
                    # 尝试更新组件的字体
                    if hasattr(widget, 'widget'):
                        widget.widget.configure(font=default_font)
                except Exception as e:
                    logger.error(f"更新组件 {attr_name} 字体失败: {e}")

        # 刷新UI以应用更改
        self.root.update_idletasks()

    def on_configure(self, event):
        """窗口大小或位置变化时的处理函数"""
        # 避免初始化时的事件
        if event.widget == self.root and event.widget.winfo_exists():
            # 立即设置窗口尺寸，不依赖_window_initialized标记
            current_width = self.root.winfo_width()
            current_height = self.root.winfo_height()
            
            # 确保窗口尺寸不小于最小值
            min_width = 800
            min_height = 600
            default_width = 1024
            default_height = 768
            
            if current_width < min_width or current_height < min_height:
                # 如果窗口尺寸过小，调整到默认值
                logger.warning(f'窗口尺寸过小 ({current_width}x{current_height})，调整到默认值 {default_width}x{default_height}')
                self.root.geometry(f'{default_width}x{default_height}')
                return
            
            # 检查窗口是否已经完全初始化
            if hasattr(self, '_window_initialized'):
                # 延迟保存配置，避免频繁保存
                self.root.after(1000, self._save_window_config)

    def _save_window_config(self):
        """保存窗口配置"""
        # 获取当前窗口大小和位置
        width = self.root.winfo_width()
        height = self.root.winfo_height()
        x = self.root.winfo_x()
        y = self.root.winfo_y()
        
        # 确保保存的窗口尺寸合理（不保存过小的窗口尺寸）
        min_width = 400
        min_height = 300
        
        if width >= min_width and height >= min_height:
            # 更新配置字典（使用与默认配置相同的字典格式）
            window_config = {
                'window_size': {'width': width, 'height': height},
                'window_position': {'x': x, 'y': y}
            }
            
            # 应用配置更新
            self.config_manager.set_many(window_config)

    def on_closing(self):
        """窗口关闭时的处理函数"""
        # 保存窗口配置
        self._save_window_config()
        # 保存其他配置
        self.save_config()
        # 关闭窗口
        self.root.destroy()

    def save_config(self):
        # 更新配置
        # 获取当前扫描模式
        scan_mode = self.scan_mode_var.get()
        
        update_config = {
            'directories': self.directory_list,
            'scan_depth': REVERSE_DEPTH_MAPPING.get(self.depth_combobox.get(), 'recursive'),
            'hash_algorithm': REVERSE_ALGORITHM_MAPPING.get(self.algorithm_combobox.get(), 'md5'),
            'sort_by': REVERSE_SORT_MAPPING.get(self.sort_combobox.get(), 'modified_time'),
            'sort_order': REVERSE_ORDER_MAPPING.get(self.order_combobox.get(), 'descending'),
            'show_empty_files': self.show_empty_var.get(),
            'delete_confirmation': self.delete_confirm_var.get(),
            'scan_mode': scan_mode,  # 新的扫描模式配置
            'scan_directory_mode': (scan_mode == 'directory'),  # 向后兼容
            'compare_filename': self.compare_filename_var.get(),
            'compare_size': self.compare_size_var.get(),
            # 相似度比对设置（新增）
            'use_similarity_comparison': self.use_similarity_var.get(),
            'similarity_threshold': self.similarity_threshold_var.get()
        }

        # 应用配置更新
        errors = self.config_manager.set_many(update_config)
        if errors:
            error_msg = '\n'.join([f'{k}: {v}' for k, v in errors.items()])
            messagebox.showerror('配置错误', f'配置更新错误:\n{error_msg}')
            return

        # 保存配置
        if self.config_manager.save_config():
            self.status_var.set('配置已保存')
            messagebox.showinfo('信息', '配置已保存')
        else:
            self.status_var.set('保存配置失败')
            messagebox.showerror('错误', '保存配置失败')

    def reset_config(self):
        # 重置为默认配置
        self.config_manager.reset_to_default()
        self.config = self.config_manager.get_config()

        # 更新UI
        self.scan_depth_var.set(self.config['scan_depth'])
        self.hash_algorithm_var.set(self.config['hash_algorithm'])
        self.sort_by_var.set(self.config['sort_by'])
        self.sort_order_var.set(self.config['sort_order'])
        self.show_empty_var.set(self.config['show_empty_files'])
        self.delete_confirm_var.set(self.config['delete_confirmation'])
        
        # 更新扫描模式
        scan_mode = self.config.get('scan_mode', 'file')
        self.scan_mode_var.set(scan_mode)
        
        # 更新对应的combobox显示值
        self.depth_combobox.set(DEPTH_MAPPING.get(self.config['scan_depth'], '递归'))
        self.algorithm_combobox.set(ALGORITHM_MAPPING.get(self.config['hash_algorithm'], 'MD5'))
        self.sort_combobox.set(SORT_MAPPING.get(self.config['sort_by'], '修改时间'))
        self.order_combobox.set(ORDER_MAPPING.get(self.config['sort_order'], '降序'))
        
        # 触发界面联动更新
        self.on_scan_mode_changed()

        self.status_var.set('配置已重置为默认值')
        messagebox.showinfo('信息', '配置已重置为默认值')

    def toggle_item_check(self, item):
        """切换单个项目的选择状态"""
        if not item:
            return
            
        # 检查是否是组头
        if item in self.group_headers.values():
            group_index = self._get_group_index_by_header(item)
            if group_index is not None:
                self._toggle_group_check(group_index)
            return
        
        # 处理单个文件项
        current_tags = self.result_tree.item(item, 'tags')
        
        if item in self.checked_items:
            # 取消选择
            del self.checked_items[item]
            new_tags = [tag for tag in current_tags if tag != 'checked']
            if 'unchecked' not in new_tags:
                new_tags.append('unchecked')
            self.result_tree.item(item, text='☐', tags=new_tags)
        else:
            # 选择
            self.checked_items[item] = True
            new_tags = [tag for tag in current_tags if tag != 'unchecked']
            if 'checked' not in new_tags:
                new_tags.append('checked')
            self.result_tree.item(item, text='☑', tags=new_tags)
        
        # 更新组头状态
        if item in self.item_to_group:
            group_index = self.item_to_group[item]
            self._update_group_header_state(group_index)
        
        # 更新统计信息
        self._update_selection_stats()
    
    def _toggle_group_check(self, group_index):
        """切换整组的选择状态"""
        if group_index >= len(self.group_tags):
            return
            
        group_items = self.group_tags[group_index]
        if not group_items:
            return
            
        # 检查当前组的选择状态
        selected_count = sum(1 for item in group_items if item in self.checked_items)
        total_count = len(group_items)
        
        if selected_count == total_count:
            # 全部选中，执行取消全选
            self.deselect_group(group_index)
        else:
            # 不是全部选中，执行全选
            self.select_group(group_index)
    
    def _update_group_header_state(self, group_index):
        """更新组头的选择状态显示"""
        if group_index not in self.group_headers:
            return
            
        header_item = self.group_headers[group_index]
        group_items = self.group_tags[group_index]
        
        selected_count = sum(1 for item in group_items if item in self.checked_items)
        total_count = len(group_items)
        
        if selected_count == 0:
            # 全部未选中
            self.result_tree.item(header_item, text='☐')
            self.group_checkboxes[header_item] = 'none'
        elif selected_count == total_count:
            # 全部选中
            self.result_tree.item(header_item, text='☑')
            self.group_checkboxes[header_item] = 'all'
        else:
            # 部分选中
            self.result_tree.item(header_item, text='◐')
            self.group_checkboxes[header_item] = 'partial'

    def smart_select(self):
        """智能选择：为每组选择建议删除的文件（保留最新或最有价值的文件）"""
        self.checked_items.clear()
        
        # 获取当前扫描模式
        scan_mode = self.scan_mode_var.get()
        is_empty_directory_mode = (scan_mode == 'empty_directory')
        
        for group_index, group in enumerate(self.duplicate_groups):
            if len(group) < 1:  # 空目录模式允许单个项目
                continue
            
            if is_empty_directory_mode:
                # 空目录模式：全选所有空目录
                group_items = self.group_tags[group_index]
                for item in group_items:
                    self.checked_items[item] = True
                    current_tags = self.result_tree.item(item, 'tags')
                    new_tags = [tag for tag in current_tags if tag != 'unchecked']
                    if 'checked' not in new_tags:
                        new_tags.append('checked')
                    self.result_tree.item(item, text='☑', tags=new_tags)
            else:
                # 重复文件模式：保留最新的文件，选择其他文件删除
                if len(group) < 2:
                    continue
                    
                # 智能选择逻辑：保留修改时间最新的文件
                sorted_files = sorted(group, key=lambda x: x.get('modified_time', 0), reverse=True)
                keep_file_path = sorted_files[0]['path']
                
                # 选择其他文件
                group_items = self.group_tags[group_index]
                for item in group_items:
                    file_path = self.result_tree.set(item, '#2')
                    if file_path != keep_file_path:
                        self.checked_items[item] = True
                        current_tags = self.result_tree.item(item, 'tags')
                        new_tags = [tag for tag in current_tags if tag != 'unchecked']
                        if 'checked' not in new_tags:
                            new_tags.append('checked')
                        self.result_tree.item(item, text='☑', tags=new_tags)
                    else:
                        # 确保保留的文件未被选中
                        if item in self.checked_items:
                            del self.checked_items[item]
                        current_tags = self.result_tree.item(item, 'tags')
                        new_tags = [tag for tag in current_tags if tag != 'checked']
                        if 'unchecked' not in new_tags:
                            new_tags.append('unchecked')
                        self.result_tree.item(item, text='☐', tags=new_tags)
            
            # 更新组头状态
            self._update_group_header_state(group_index)
        
        self._update_selection_stats()
        
        if is_empty_directory_mode:
            logger.info('已执行智能选择：选中所有空目录删除')
        else:
            logger.info('已执行智能选择：保留最新文件，选择其他文件删除')
    
    def smart_select_group(self, group_index):
        """对单个组执行智能选择"""
        if group_index >= len(self.duplicate_groups):
            return
            
        group = self.duplicate_groups[group_index]
        if len(group) < 2:
            return
            
        # 智能选择逻辑：保留修改时间最新的文件
        sorted_files = sorted(group, key=lambda x: x.get('modified_time', 0), reverse=True)
        keep_file_path = sorted_files[0]['path']
        
        # 选择其他文件
        group_items = self.group_tags[group_index]
        for item in group_items:
            file_path = self.result_tree.set(item, '#2')
            if file_path != keep_file_path:
                self.checked_items[item] = True
                current_tags = self.result_tree.item(item, 'tags')
                new_tags = [tag for tag in current_tags if tag != 'unchecked']
                if 'checked' not in new_tags:
                    new_tags.append('checked')
                self.result_tree.item(item, text='☑', tags=new_tags)
            else:
                # 确保保留的文件未被选中
                if item in self.checked_items:
                    del self.checked_items[item]
                current_tags = self.result_tree.item(item, 'tags')
                new_tags = [tag for tag in current_tags if tag != 'checked']
                if 'unchecked' not in new_tags:
                    new_tags.append('unchecked')
                self.result_tree.item(item, text='☐', tags=new_tags)
        
        # 更新组头状态
        self._update_group_header_state(group_index)
        self._update_selection_stats()

    def select_group(self, group_index):
        """选择整组文件"""
        if group_index >= len(self.group_tags):
            return
            
        group_items = self.group_tags[group_index]
        for item in group_items:
            self.checked_items[item] = True
            current_tags = self.result_tree.item(item, 'tags')
            new_tags = [tag for tag in current_tags if tag != 'unchecked']
            if 'checked' not in new_tags:
                new_tags.append('checked')
            self.result_tree.item(item, text='☑', tags=new_tags)
        
        self._update_group_header_state(group_index)
        self._update_selection_stats()
    
    def deselect_group(self, group_index):
        """取消选择整组文件"""
        if group_index >= len(self.group_tags):
            return
            
        group_items = self.group_tags[group_index]
        for item in group_items:
            if item in self.checked_items:
                del self.checked_items[item]
            current_tags = self.result_tree.item(item, 'tags')
            new_tags = [tag for tag in current_tags if tag != 'checked']
            if 'unchecked' not in new_tags:
                new_tags.append('unchecked')
            self.result_tree.item(item, text='☐', tags=new_tags)
        
        self._update_group_header_state(group_index)
        self._update_selection_stats()

    def _update_selection_stats(self):
        """更新选择统计信息"""
        selected_count = len(self.checked_items)
        total_size = 0
        
        for item in self.checked_items:
            values = self.result_tree.item(item, 'values')
            if len(values) >= 3:
                size_str = values[2]  # 文件大小在第3列
                # 从格式化的大小字符串中提取数值（简化处理）
                try:
                    if 'KB' in size_str:
                        total_size += float(size_str.replace(' KB', '')) * 1024
                    elif 'MB' in size_str:
                        total_size += float(size_str.replace(' MB', '')) * 1024 * 1024
                    elif 'GB' in size_str:
                        total_size += float(size_str.replace(' GB', '')) * 1024 * 1024 * 1024
                    elif 'B' in size_str:
                        total_size += float(size_str.replace(' B', ''))
                except:
                    pass
        
        if selected_count > 0:
            formatted_size = self.format_size(int(total_size))
            self.selection_stats_label.config(text=f'已选择: {selected_count} 个文件, 总大小: {formatted_size}')
        else:
            self.selection_stats_label.config(text='')
        
        # 更新状态栏
        if hasattr(self, 'duplicate_groups') and self.duplicate_groups:
            total_files = sum(len(group) for group in self.duplicate_groups)
            self.stats_label.config(text=f'扫描结果: {len(self.duplicate_groups)} 组重复文件, 共 {total_files} 个文件')

if __name__ == '__main__':
    root = tk.Tk()
    app = DuplicateFileFinderUI(root)
    root.mainloop()